package cn.virens;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import cn.hutool.core.util.RandomUtil;

public class Main {
	private static final ReentrantReadWriteLock mapLock = new ReentrantReadWriteLock();
	private static final Map<String, Value> map = new HashMap<>();

	private static Value get(String id) {
		Value answer = null;

		mapLock.readLock().lock();

		try {
			answer = map.get(id);

			if (answer == null) {
				mapLock.readLock().unlock();
				mapLock.writeLock().lock();

				try {
					answer = new Value(RandomUtil.randomInt(5, 15));
					answer.setId(id);
					answer.setState(0);
					answer.setNumber(0);

					map.put(id, answer);
				} finally {
					mapLock.writeLock().unlock();
					mapLock.readLock().lock();
				}
			}
		} finally {
			mapLock.readLock().unlock();
		}

		return answer;
	}

	public static void main(String[] args) {
		for (int i = 0; i < 30; i++) {
			new Thread(() -> {
				while (true) {
					for (int j = 0; j < 10; j++) {
						Value value = get("ID-" + j);

						if (value.vaild()) {
							value.lock.writeLock().lock();

							try {
								value.setNumber(value.getNumber() + 1);

								if (value.getNumber() == value.max) {
									value.setState(1);
								}
							} finally {
								value.lock.writeLock().unlock();
							}
						}
					}

					try {
						Thread.sleep(1000 * 5);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

			}).start();
		}

		while (true) {
			for (Value value : map.values()) {
				System.out.println(value.toString());
			}

			try {
				Thread.sleep(1000 * 2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	private static class Value implements Serializable {
		private static final long serialVersionUID = 6516093964652300160L;
		private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
		private final AtomicInteger integer = new AtomicInteger();
		private final long timeMillis = System.currentTimeMillis();
		private final int max;

		private String id;
		private Integer state;
		private Integer number;

		public Value(int max) {
			this.max = max;
		}

		public boolean vaild() {
			return integer.incrementAndGet() <= max;
		}

		public void setId(String id) {
			this.id = id;
		}

		public void setState(Integer state) {
			this.state = state;
		}

		public Integer getNumber() {
			return number;
		}

		public void setNumber(Integer number) {
			this.number = number;
		}

		@Override
		public String toString() {
			return String.format("Value(%d)[id=%s, state=%d, number=%d]", timeMillis, id, state, number);
		}
	}

}
